# 整体模块实现

import numpy as np
from utils.features import prepare_for_training  # 导入数据预处理函数

# 线性回归
class LinearRegression:

    def __init__(self, data, labels, polynomial_degree=0, sinusoid_degree=0, normalize_data=True, ) -> None:
        """
            1. 对数据进行预处理
            2. 得到所有的特征个数
            3. 初始化参数矩阵
        """
        """数据预处理，得到处理后的数据，数据的均值，数据的标准差"""
        (data_processed, features_mean, features_deviation) = \
            prepare_for_training(data, polynomial_degree=0, sinusoid_degree=0, normalize_data=True)

        self.data = data_processed  # 使用预处理以后的值
        self.labels = labels        # 标签值
        self.features_mean = features_mean  # 数据的均值
        self.features_deviation = features_deviation  # 数据的方差
        self.polynomial_degree = polynomial_degree  # 变换维度
        self.sinusoid_degree = sinusoid_degree      # sin变换的维度
        self.normalize_data = normalize_data        # 标准化后的数据
        
        num_feature = self.data.shape[1]  # 获取数据的列数，表示 自变量的个数，也就是维度
        self.theta = np.zeros((num_feature, 1))  # 做出来了一个 与自变量个数相等的 θ 矩阵


    """梯度下降"""
    def gradient_desent(self, alpha, num_iterations):
        cost_history = []  # 损失值
        for i in range(num_iterations):
            self.gradient_step(alpha)
            cost_history.append(self.cost_function(self.data, self.labels))
        return cost_history


    """参数更新"""
    def gradient_step(self, alpha):
        num_examples = self.data.shape[0]  # 样本个数
        prediction = LinearRegression.hypothesis(self.data, self.theta)  # 求取预测值
        delta = prediction - self.labels  # 预测值减去真实值得到误差
        # 参数更新，使用小批量梯度下降
        theta = self.theta
        theta = theta - alpha * (1 / num_examples) * (np.dot(delta.T, self.data)).T
        self.theta = theta


    """预测函数，h(x) = θ ^T * x"""
    @staticmethod
    def hypothesis(data, theta):
        predictions = np.dot(data, theta)
        return predictions


    """损失函数"""
    def cost_function(self, data, labels):
        num_examples = data.shape[0]  # 样本个数
        delta = LinearRegression.hypothesis(self.data, self.theta) - labels  # 求取预测值 - 真实值
        cost = (1 / 2) * np.dot(delta.T, delta) / num_examples
        return cost[0][0]

    """得到当前损失"""
    def get_cost(self, data, labels):
        # 处理完之后的数据
        data_process = prepare_for_training(data, self.polynomial_degree, 
            self.sinusoid_degree, 
            self.normalize_data)[0]
        return self.cost_function(data_process, labels)
    

    """用训练好的参数模型，与预测得到回归值结果"""
    def prodict(self, data):
        # 处理完之后的数据
        data_process = prepare_for_training(data, self.polynomial_degree, self.sinusoid_degree, self.normalize_data)[0]
        return LinearRegression.hypothesis(data_process, self.theta)

    """训练,执行梯度下降"""
    def train(self, alpha, num_iterations):
        """ 
            alpha:学习率或者步长
            num_iterations: 迭代次数
        """
        cost_history = self.gradient_desent(alpha, num_iterations)
        return self.theta, cost_history
